---
title: FileSystem (legacy)
description: A library that provides access to the local file system on the device.
sourceCodeUrl: 'https://github.com/expo/expo/tree/main/packages/expo-file-system/legacy'
packageName: 'expo-file-system'
iconUrl: '/static/images/packages/expo-file-system.png'
platforms: ['android', 'ios', 'tvos']
---

import APISection from '~/components/plugins/APISection';
import { APIInstallSection } from '~/components/plugins/InstallSection';
import { AndroidPermissions } from '~/components/plugins/permissions';
import { Collapsible } from '~/ui/components/Collapsible';
import { ContentSpotlight } from '~/ui/components/ContentSpotlight';
import { SnackInline } from '~/ui/components/Snippet';

> **important** The `legacy` version of the FileSystem API is included in the `expo-file-system` library. It can be used alongside the modern API for backward compatibility reasons.

`expo-file-system` provides access to a file system stored locally on the device. It is also capable of uploading and downloading files from network URLs.

<Collapsible summary="Diagram explaining how expo-file-system interacts with different resources">
  <ContentSpotlight
    alt="Diagram of the various pieces of expo-file-system and how they interact with different resources"
    src="/static/images/sdk/file-system/file-system-diagram.png"
    containerClassName="bg-palette-white"
  />
</Collapsible>

<Collapsible summary="How expo-file-system works differently inside of the Expo Go app">
  Within Expo Go, each project has a separate file system scope and has no access to the file system
  of other projects.
</Collapsible>

## Installation

<APIInstallSection />

## Usage

### Downloading files

```js Component.js
const callback = downloadProgress => {
  const progress = downloadProgress.totalBytesWritten / downloadProgress.totalBytesExpectedToWrite;
  this.setState({
    downloadProgress: progress,
  });
};

const downloadResumable = FileSystem.createDownloadResumable(
  'http://techslides.com/demos/sample-videos/small.mp4',
  FileSystem.documentDirectory + 'small.mp4',
  {},
  callback
);

try {
  const { uri } = await downloadResumable.downloadAsync();
  console.log('Finished downloading to ', uri);
} catch (e) {
  console.error(e);
}

try {
  await downloadResumable.pauseAsync();
  console.log('Paused download operation, saving for future retrieval');
  AsyncStorage.setItem('pausedDownload', JSON.stringify(downloadResumable.savable()));
} catch (e) {
  console.error(e);
}

try {
  const { uri } = await downloadResumable.resumeAsync();
  console.log('Finished downloading to ', uri);
} catch (e) {
  console.error(e);
}

//To resume a download across app restarts, assuming the DownloadResumable.savable() object was stored:
const downloadSnapshotJson = await AsyncStorage.getItem('pausedDownload');
const downloadSnapshot = JSON.parse(downloadSnapshotJson);
const downloadResumable = new FileSystem.DownloadResumable(
  downloadSnapshot.url,
  downloadSnapshot.fileUri,
  downloadSnapshot.options,
  callback,
  downloadSnapshot.resumeData
);

try {
  const { uri } = await downloadResumable.resumeAsync();
  console.log('Finished downloading to ', uri);
} catch (e) {
  console.error(e);
}
```

### Managing Giphy's

<SnackInline
  label="Giphy example"
  templateId="filesystem/App"
  dependencies={['expo-file-system']}
  files={{
    'GifFetching.ts': 'filesystem/gifFetching.ts',
    'GifManagement.ts': 'filesystem/gifManagement.ts'
  }}
>

```js
import * as FileSystem from 'expo-file-system/legacy';

const gifDir = FileSystem.cacheDirectory + 'giphy/';
const gifFileUri = (gifId: string) => gifDir + `gif_${gifId}_200.gif`;
const gifUrl = (gifId: string) => `https://media1.giphy.com/media/${gifId}/200.gif`;

// Checks if gif directory exists. If not, creates it
async function ensureDirExists() {
  const dirInfo = await FileSystem.getInfoAsync(gifDir);
  if (!dirInfo.exists) {
    console.log("Gif directory doesn't exist, creating…");
    await FileSystem.makeDirectoryAsync(gifDir, { intermediates: true });
  }
}

// Downloads all gifs specified as array of IDs
export async function addMultipleGifs(gifIds: string[]) {
  try {
    await ensureDirExists();

    console.log('Downloading', gifIds.length, 'gif files…');
    await Promise.all(gifIds.map(id => FileSystem.downloadAsync(gifUrl(id), gifFileUri(id))));
  } catch (e) {
    console.error("Couldn't download gif files:", e);
  }
}

// Returns URI to our local gif file
// If our gif doesn't exist locally, it downloads it
export async function getSingleGif(gifId: string) {
  await ensureDirExists();

  const fileUri = gifFileUri(gifId);
  const fileInfo = await FileSystem.getInfoAsync(fileUri);

  if (!fileInfo.exists) {
    console.log("Gif isn't cached locally. Downloading…");
    await FileSystem.downloadAsync(gifUrl(gifId), fileUri);
  }

  return fileUri;
}

// Exports shareable URI - it can be shared outside your app
export async function getGifContentUri(gifId: string) {
  return FileSystem.getContentUriAsync(await getSingleGif(gifId));
}

// Deletes whole giphy directory with all its content
export async function deleteAllGifs() {
  console.log('Deleting all GIF files…');
  await FileSystem.deleteAsync(gifDir);
}
```

</SnackInline>

### Server: handling multipart requests

The simple server in Node.js, which can save uploaded images to disk:

```js index.js
const express = require('express');
const app = express();
const fs = require('fs');
const multer = require('multer');
const upload = multer({ dest: 'uploads/' });

// This method will save the binary content of the request as a file.
app.patch('/binary-upload', (req, res) => {
  req.pipe(fs.createWriteStream('./uploads/image' + Date.now() + '.png'));
  res.end('OK');
});

// This method will save a "photo" field from the request as a file.
app.patch('/multipart-upload', upload.single('photo'), (req, res) => {
  // You can access other HTTP parameters. They are located in the body object.
  console.log(req.body);
  res.end('OK');
});

app.listen(3000, () => {
  console.log('Working on port 3000');
});
```

## API

```js
import * as FileSystem from 'expo-file-system/legacy';
```

### Directories

The API takes `file://` URIs pointing to local files on the device to identify files. Each app only has read and write access to locations under the following directories:

- [`FileSystem.documentDirectory`](#filesystemdocumentdirectory)
- [`FileSystem.cacheDirectory`](#filesystemcachedirectory)

So, for example, the URI to a file named `'myFile'` under `'myDirectory'` in the app's user documents directory would be `FileSystem.documentDirectory + 'myDirectory/myFile'`.

Expo APIs that create files generally operate within these directories. This includes `Audio` recordings, `Camera` photos, `ImagePicker` results, `SQLite` databases and `takeSnapShotAsync()` results. This allows their use with the `FileSystem` API.

Some `FileSystem` functions are able to read from (but not write to) other locations.

### SAF URI

A SAF URI is a URI that is compatible with the Storage Access Framework. It should look like this `content://com.android.externalstorage.*`.
The easiest way to obtain such URI is by [`requestDirectoryPermissionsAsync`](#requestdirectorypermissionsasyncinitialfileurl) method.

<APISection packageName="expo-file-system-legacy" apiName="FileSystem Legacy" />

## Supported URI schemes

In this table, you can see what type of URI can be handled by each method. For example, if you have an URI, which begins with `content://`, you cannot use `FileSystem.readAsStringAsync()`, but you can use `FileSystem.copyAsync()` which supports this scheme.

| Method name               | Android                                                                                                                              | iOS                                                                                             |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------- |
| `getInfoAsync`            | `file:///`,<br/>`content://`,<br/>`asset://`,<br/>no scheme                                                                          | `file://`,<br/>`ph://`,<br/>`assets-library://`                                                 |
| `readAsStringAsync`       | `file:///`,<br/>`asset://`,<br/>[SAF URI](#saf-uri)                                                                                  | `file://`                                                                                       |
| `writeAsStringAsync`      | `file:///`,<br/>[SAF URI](#saf-uri)                                                                                                  | `file://`                                                                                       |
| `deleteAsync`             | `file:///`,<br/>[SAF URI](#saf-uri)                                                                                                  | `file://`                                                                                       |
| `moveAsync`               | Source:<br/>`file:///`,<br/>[SAF URI](#saf-uri)<br/><br/>Destination:<br/>`file://`                                                  | Source:<br/>`file://`<br/><br/>Destination:<br/>`file://`                                       |
| `copyAsync`               | Source:<br/>`file:///`,<br/>`content://`,<br/>`asset://`,<br/>[SAF URI](#saf-uri),<br/>no scheme<br/><br/>Destination:<br/>`file://` | Source:<br/>`file://`,<br/>`ph://`,<br/>`assets-library://`<br/><br/>Destination:<br/>`file://` |
| `makeDirectoryAsync`      | `file:///`                                                                                                                           | `file://`                                                                                       |
| `readDirectoryAsync`      | `file:///`                                                                                                                           | `file://`                                                                                       |
| `downloadAsync`           | Source:<br/>`http://`,<br/>`https://`<br/><br/>Destination:<br/>`file:///`                                                           | Source:<br/>`http://`,<br/>`https://`<br/><br/>Destination:<br/>`file://`                       |
| `uploadAsync`             | Source:<br/>`file:///`<br/><br/>Destination:<br/>`http://`<br/>`https://`                                                            | Source:<br/>`file://`<br/><br/>Destination:<br/>`http://`<br/>`https://`                        |
| `createDownloadResumable` | Source:<br/>`http://`,<br/>`https://`<br/><br/>Destination:<br/>`file:///`                                                           | Source:<br/>`http://`,<br/>`https://`<br/><br/>Destination:<br/>`file://`                       |

> On Android **no scheme** defaults to a bundled resource.

## Permissions

### Android

The following permissions are added automatically through this library's **AndroidManifest.xml**.

<AndroidPermissions permissions={['READ_EXTERNAL_STORAGE', 'WRITE_EXTERNAL_STORAGE', 'INTERNET']} />

### iOS

_No permissions required_.
